<script setup>
import { ref, reactive, computed, defineComponent, onMounted, onBeforeUnmount, watch, nextTick, h } from 'vue';
import EditorTabs from './EditorTabs.vue';
import TabContent from './NewTabContent.vue';
import SettingsContent from '../SettingsPanel/SettingsContent.vue';
import { openContextMenu } from '../../assets/menus';
import { registerScrollElement } from '../../assets/scrollSync';
import { useWorkspaceStore } from '../../stores/workspace';
import { formatCode, getGitHistoryContent, getLocalHistoryContent, localHistoryRestore, get_timeline } from '../../tauriApi';
import { addLocalHistorySnapshot } from '../../assets/local-history';
import { addRecentFile } from '../../assets/recents';
import { invoke } from '@tauri-apps/api/core';
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
import 'monaco-editor/min/vs/editor/editor.main.css';
import 'monaco-editor/esm/vs/language/json/monaco.contribution';
import 'monaco-editor/esm/vs/language/css/monaco.contribution';
import 'monaco-editor/esm/vs/language/html/monaco.contribution';
import 'monaco-editor/esm/vs/language/typescript/monaco.contribution';
import 'monaco-editor/esm/vs/basic-languages/markdown/markdown.contribution';
import 'monaco-editor/esm/vs/basic-languages/rust/rust.contribution';
import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution';
import 'monaco-editor/esm/vs/basic-languages/typescript/typescript.contribution';
import 'monaco-editor/esm/vs/basic-languages/html/html.contribution';
import 'monaco-editor/esm/vs/basic-languages/css/css.contribution';
// 额外的语言支持
import 'monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution';
import 'monaco-editor/esm/vs/basic-languages/go/go.contribution';
import 'monaco-editor/esm/vs/basic-languages/cpp/cpp.contribution';
import 'monaco-editor/esm/vs/basic-languages/csharp/csharp.contribution';
import 'monaco-editor/esm/vs/basic-languages/java/java.contribution';
import 'monaco-editor/esm/vs/basic-languages/dart/dart.contribution';
import 'monaco-editor/esm/vs/basic-languages/php/php.contribution';
import 'monaco-editor/esm/vs/basic-languages/ruby/ruby.contribution';
import 'monaco-editor/esm/vs/basic-languages/swift/swift.contribution';
import 'monaco-editor/esm/vs/basic-languages/ini/ini.contribution';
import 'monaco-editor/esm/vs/basic-languages/xml/xml.contribution';
import 'monaco-editor/esm/vs/basic-languages/dockerfile/dockerfile.contribution';
import 'monaco-editor/esm/vs/basic-languages/powershell/powershell.contribution';
import * as monacoYaml from 'monaco-yaml';
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import CssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
import HtmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
import TsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
import YamlWorker from 'monaco-yaml/yaml.worker?worker';
import 'monaco-editor/esm/vs/basic-languages/python/python.contribution';
import 'monaco-editor/esm/vs/basic-languages/shell/shell.contribution';
/** @typedef {import('../../types/editor').EditorTab} EditorTab */
/** @typedef {import('../../types/editor').DiffModels} DiffModels */
/** @typedef {import('../../stores/editor').EditorGroup} EditorGroup */
import { useEditorStore } from '../../stores/editor';
import { useProblemsStore } from '../../stores/problems';
import { useOutputStore } from '../../stores/output';
import { getDiagnosticService } from '../../services/diagnosticService';
import { fileOpsService } from '../../services/FileOpsService';
import { useGlobalAiTimelineWatcher } from './composables/useAiTimelineWatcher';
import { useActiveTabTracker } from './composables/useActiveTabTracker';
import { useEditorModeController } from './composables/useEditorModeController';
// 已移除 normalizePathKey 导入 - 简化编辑器，不再需要路径标准化

let editor = null;
let diffEditor = null;
// 已移除调试diff编辑器

const props = defineProps({ bottomPad: { type: Number, default: 0 } });

// Monaco worker 环境配置
self.MonacoEnvironment = {
  getWorker(_, label) {
    if (label === 'json') return new JsonWorker();
    if (label === 'css') return new CssWorker();
    if (label === 'html') return new HtmlWorker();
  if (label === 'yaml') return new YamlWorker(); // monaco-yaml worker 用于 LSP/诊断
    if (label === 'typescript' || label === 'javascript') return new TsWorker();
    return new EditorWorker();
  }
};

// 辅助函数
function langFromExt(ext) {
  switch ((ext || '').toLowerCase()) {
    case 'js': case 'cjs': case 'mjs': case 'jsx': return 'javascript';
    case 'ts': case 'tsx': return 'typescript';
    case 'json': return 'json';
    case 'css': case 'scss': case 'sass': return 'css';
    case 'html': case 'vue': return 'html';
    case 'md': case 'markdown': return 'markdown';
    case 'rs': return 'rust';
    case 'py': return 'python';
    case 'sh': case 'bash': return 'shell';
    case 'go': return 'go';
    case 'c': return 'cpp';
    case 'h': return 'cpp';
    case 'hpp': case 'hh': case 'hxx': case 'cpp': case 'cc': case 'cxx': return 'cpp';
    case 'cs': return 'csharp';
    case 'java': return 'java';
    case 'dart': return 'dart';
    case 'php': return 'php';
    case 'rb': return 'ruby';
    case 'swift': return 'swift';
    case 'xml': return 'xml';
    case 'yml': case 'yaml': return 'yaml';
    case 'ini': case 'conf': case 'cfg': case 'editorconfig': return 'ini';
    case 'toml': return 'ini';
    case 'dockerfile': case 'docker': return 'dockerfile';
    default: return 'plaintext';
  }
}
function uid(prefix='id') { return prefix + '-' + Math.random().toString(36).slice(2,8); }

// normalizePathKey 已移动到 ../../utils/path

// 在整个布局树中通过标准化路径查找现有标签页
function findTabByNormalizedPath(node, normalizedPath) {
  if (!node) return null;
  if (node.type === 'group') {
    const tab = (node.tabs || []).find(t => t.path === normalizedPath);
    return tab ? { group: node, tab } : null;
  }
  for (const c of (node.children || [])) {
    const r = findTabByNormalizedPath(c, normalizedPath);
    if (r) return r;
  }
  return null;
}

// 收集所有标签页（用于调试路径标准化和重用）
function __debugCollectAllTabs(node, out = []) {
  if (!node) return out;
  if (node.type === 'group') {
    for (const t of (node.tabs || [])) {
      out.push({ groupId: node.id, tabId: t.id, path: t.path, normalized: t.path });
    }
    return out;
  }
  for (const c of (node.children || [])) __debugCollectAllTabs(c, out);
  return out;
}

// 已移除AI时间线辅助函数 - 简化编辑器，不再支持diff功能

// 已移除diff显示功能 - 简化编辑器，不再支持diff

/** @param {string} filePath 
 *  @returns {string}
 */
function getLanguageFromPath(filePath) {
  if (!filePath) return 'plaintext';
  const ext = filePath.split('.').pop()?.toLowerCase();
  const langMap = {
    'js': 'javascript',
    'ts': 'typescript',
    'vue': 'vue',
    'html': 'html',
    'css': 'css',
    'scss': 'scss',
    'json': 'json',
    'md': 'markdown',
    'py': 'python',
    'java': 'java',
    'cpp': 'cpp',
    'c': 'c'
  };
  return langMap[ext] || 'plaintext';
}
/** @param {EditorTab} tab 
 *  @returns {monaco.Uri}
 */
function getModelUriForTab(tab) {
  if (tab && tab.path) return monaco.Uri.file(String(tab.path));
  return monaco.Uri.parse('inmemory://model/' + String(tab.id)); // 内存中的模型URI
}

// 已移除 renderDebugDiff

/**
 * @param {string} groupId
 * @param {EditorTab} tab
 * @param {string} content
 */
function setTabModelContent(groupId, tab, content) {
  console.log('[setTabModelContent] Setting content for tab:', tab.id, 'content length:', content?.length || 0);
  console.log('[setTabModelContent] Tab before update - dirty:', tab.dirty, 'savedContent length:', (tab.savedContent || '').length);
  editorStore.setActiveFileContent(content);
  try {
    const uri = getModelUriForTab(tab);
    const key = uri.toString();
    let model = modelsByPath.get(key) || monaco.editor.getModel(uri);
    const lang = langFromExt(tab.ext);
    
    // 使用Vue的响应式赋值确保响应性
    const newContent = String(content || '');
    // console.log('[setTabModelContent] Before update - tab.content length:', tab.content?.length || 0);
    Object.assign(tab, {
      content: newContent,
      savedContent: newContent
    });
    // console.log('[setTabModelContent] After update - tab.content length:', tab.content.length);
    // console.log('[setTabModelContent] Tab object after update:', { id: tab.id, content: tab.content?.substring(0, 50) + '...' });
    // console.log('[setTabModelContent] After update - tab.content length:', tab.content.length);
    // console.log('[setTabModelContent] Tab object after update:', { id: tab.id, content: tab.content?.substring(0, 50) + '...' });
    if (!model) {
      model = monaco.editor.createModel(tab.content, lang, uri);
      modelsByPath.set(key, model);
      // console.log('[setTabModelContent] Created new model for tab:', tab.id);
      // console.log('[setTabModelContent] Created new model for tab:', tab.id);
    } else {
      if (model.getValue() !== tab.content) {
        model.setValue(tab.content);
        // console.log('[setTabModelContent] Updated existing model for tab:', tab.id);
        // console.log('[setTabModelContent] Updated existing model for tab:', tab.id);
      }
      try { monaco.editor.setModelLanguage(model, lang); } catch {}
    }
    // 已移除diff编辑器逻辑 - 简化编辑器，不再支持diff功能
    const ed = editorsByGroup.get(groupId);
    const group = findGroup(layoutRoot.value, groupId);
    if (ed && group && group.activeId === tab.id) {
      ed.setModel(model);
    }
    // 确保内容加载后正确设置脏状态
    console.log('[setTabModelContent] Setting tab.dirty to false for tab:', tab.id);
    tab.dirty = false;
    console.log('[setTabModelContent] Tab after setting dirty=false:', { id: tab.id, dirty: tab.dirty, contentLength: tab.content?.length || 0, savedContentLength: (tab.savedContent || '').length });
  } catch {}
}

// 布局树状态
// 分割: { id, type:'split', direction:'row'|'col', sizes:number[], children:Node[] }
// 组: { id, type:'group', tabs:Tab[], activeId?:string }
const layoutRoot = ref({ id: uid('group'), type: 'group', tabs: [], activeId: null });
const focusedGroupId = ref(layoutRoot.value.id);
const globalActiveId = ref(null);
/** @type {Set<string>} */
const openingPaths = new Set();

// 编辑器和模型（使用普通Map避免重对象的响应式开销）
/** @type {Map<string, monaco.editor.IStandaloneCodeEditor | monaco.editor.IStandaloneDiffEditor>} */
const editorsByGroup = new Map(); // groupId -> monaco编辑器（单编辑器或diff编辑器）
/** @type {Map<string, HTMLElement>} */
const containerByGroup = new Map(); // groupId -> 元素
// 跟踪每个组的编辑器级别可释放对象（diff导航器、onDidUpdateDiff等）以避免内存泄漏
/** @type {Map<string, monaco.IDisposable[]>} */
const editorDisposablesByGroup = new Map();
function cleanupEditorDisposables(groupId) {
  const arr = editorDisposablesByGroup.get(groupId);
  if (Array.isArray(arr)) {
    for (const d of arr) { try { d?.dispose?.(); } catch {} }
  }
  editorDisposablesByGroup.delete(groupId);
}
function registerEditorDisposable(groupId, disposable) {
  if (!disposable) return;
  const arr = editorDisposablesByGroup.get(groupId) || [];
  arr.push(disposable);
  editorDisposablesByGroup.set(groupId, arr);
}
/** @type {Map<string, monaco.editor.ITextModel>} */
const modelsByPath = new Map(); // path/id -> monaco模型
/** @type {Map<string, 'editor' | 'diff'>} */
const groupModeByGroupId = new Map(); // groupId -> 'editor' | 'diff'

/** @param {string} groupId 
 *  @returns {'editor' | 'diff'}
 */
function getGroupMode(groupId) {
  return groupModeByGroupId.get(groupId) || 'editor';
}
/** @param {string} groupId 
 *  @param {'editor' | 'diff' | null | undefined} mode
 */
function setGroupMode(groupId, mode) {
  groupModeByGroupId.set(groupId, mode === 'diff' ? 'diff' : 'editor');
}

// 存储设置
const editorStore = useEditorStore();
const problemsStore = useProblemsStore();
const outputStore = useOutputStore();

// 活跃标签页追踪器
const activeTabTracker = useActiveTabTracker(
  globalActiveId,
  focusedGroupId,
  findGroup,
  layoutRoot
);

// 延迟初始化诊断服务
onMounted(() => {
  // 延迟一下确保Pinia已经初始化
  setTimeout(() => {
    try {
      getDiagnosticService().collectProblems();
      console.log('Diagnostic service initialized successfully');
    } catch (error) {
      console.warn('Failed to initialize diagnostic service:', error);
    }
  }, 100);
});

// 编辑器模式控制器
const modeController = useEditorModeController();

// 全局AI时间线监听器 - 自动管理所有tab的AI记录监听
const globalAiWatcher = useGlobalAiTimelineWatcher();
const diffingGroupId = ref(null);

// 已移除调试diff面板状态
onMounted(async () => { 
  try { await editorStore.initFromSettings(); } catch {} 
  
  // 初始化全局AI时间线监听器
  await globalAiWatcher.initialize();
  
  // 设置活跃标签页追踪器和模式控制器
  globalAiWatcher.setActiveTabTracker(activeTabTracker);
  globalAiWatcher.setModeController(modeController);
  
  // 注入文件打开器：AI时间线出现新变更时自动打开对应文件（若不存在则创建标签页）
  globalAiWatcher.setFileOpener(async (path, opts = {}) => {
    try { await openFileInFocused(path, opts); } catch (e) { console.warn('[EditorArea] openFileInFocused failed from AI watcher', e); }
  });
  
  // 显式启动全局AI时间线监听（依赖注入完成后）
  try { globalAiWatcher.start(); } catch {}

  // 同步当前活跃标签页
  await globalAiWatcher.syncWithActiveTab();
  
  console.log('[EditorArea] Dynamic editor switching initialized');
});

// 已移除AI时间线监听 - 简化编辑器，不再支持diff功能

// 布局辅助函数
/**
 * @param {any} node
 * @param {string} groupId
 * @returns {EditorGroup | null}
 */
function findGroup(node, groupId) {
  if (!node) return null;
  if (node.type === 'group') return node.id === groupId ? node : null;
  for (const c of node.children) { const f = findGroup(c, groupId); if (f) return f; }
  return null;
}
/**
 * @param {any} root
 * @param {string} targetId
 * @param {any} [parent]
 * @returns {any}
 */
function findParent(root, targetId, parent=null) {
  if (!root) return null;
  if (root.id === targetId) return parent;
  if (root.type === 'split') {
    for (const c of root.children) { const p = findParent(c, targetId, root); if (p) return p; }
  }
  return null;
}
/**
 * @param {any} splitNode
 * @param {string} childId
 * @param {any} newNode
 * @returns {void}
 */
function replaceChild(splitNode, childId, newNode) {
  const idx = splitNode.children.findIndex(c => c.id === childId);
  if (idx >= 0) splitNode.children.splice(idx, 1, newNode);
}
/**
 * @param {any} node
 * @param {string[]} out
 */
function collectGroupIds(node, out) {
  if (!node) return;
  if (node.type === 'group') { out.push(node.id); return; }
  for (const c of node.children || []) collectGroupIds(c, out);
}
/**
 * @param {any} node
 * @returns {EditorGroup | null}
 */
function findGroupContainingSettings(node) {
  if (!node) return null;
  if (node.type === 'group') {
    if ((node.tabs || []).some(t => t.id === 'settings' || t.ext === 'settings')) return node;
    return null;
  }
  for (const c of node.children || []) { const g = findGroupContainingSettings(c); if (g) return g; }
  return null;
}
function firstGroup(node) {
  if (!node) return null;
  if (node.type === 'group') return node;
  for (const c of node.children || []) { const g = firstGroup(c); if (g) return g; }
  return null;
}
function refreshAllGroupsEditors() {
  nextTick(() => {
    const ids = [];
    collectGroupIds(layoutRoot.value, ids);
    for (const gid of ids) {
      try {
        // ensureEditorForGroup 调用已移除 - 现在由 TabContent 组件处理
        const g = findGroup(layoutRoot.value, gid);
        if (g && g.activeId) {
          const t = g.tabs.find(x => x.id === g.activeId);
          if (t) {
            // 在附加之前，如果模型已存在，从模型同步 tab.content
            const uri = getModelUriForTab(t);
            const m = monaco.editor.getModel(uri);
            if (m) { try { t.content = m.getValue(); } catch {} }
            // attachModelForTab 调用已移除 - 现在由 TabContent 组件处理
          }
        }
      } catch {}
    }
    scheduleLayoutAll();
  });
}
function removeChildAndCollapse(rootRef, childId) {
  const parent = findParent(rootRef.value, childId);
  if (!parent) return;
  if (parent.type !== 'split') return;
  const idx = parent.children.findIndex(c => c.id === childId);
  if (idx >= 0) parent.children.splice(idx, 1);
  // 如果只剩一个子节点则折叠
  if (parent.children.length === 1) {
    const grand = findParent(rootRef.value, parent.id);
    const only = parent.children[0];
    if (!grand) { rootRef.value = only; }
    else replaceChild(grand, parent.id, only);
  } else {
    // 重新分配大小
    const n = parent.children.length;
    parent.sizes = Array(n).fill(100 / n);
  }
  // 结构变化后，确保剩余组的编辑器重新附加
  const fg = firstGroup(rootRef.value);
  if (fg) {
    focusedGroupId.value = fg.id;
    try {
      const t = (fg.tabs || []).find(x => x.id === fg.activeId);
      if (t) {
        // ensureEditorForGroup 调用已移除 - 现在由 TabContent 组件处理
        // attachModelForTab 调用已移除 - 现在由 TabContent 组件处理
        globalActiveId.value = fg.activeId;
      }
  } catch {}
}
  refreshAllGroupsEditors();
}

// 操作
function splitGroup(groupId, direction='row') {
  const group = findGroup(layoutRoot.value, groupId);
  if (!group) return;
  // 当组中没有标签页时阻止分割
  if (!group.tabs || group.tabs.length === 0) {
    try { console.warn('[Editor] splitGroup blocked: no tabs in group', { groupId }); } catch {}
    return;
  }
  const newGroup = { id: uid('group'), type: 'group', tabs: [], activeId: null };
  // 用包含现有和新组的分割节点替换此组
  const parent = findParent(layoutRoot.value, group.id);
  const split = { id: uid('split'), type: 'split', direction, sizes: [50, 50], children: [group, newGroup] };
  if (!parent) layoutRoot.value = split;
  else replaceChild(parent, group.id, split);

  // 将当前活动标签页复制到新组中（类似VS Code的行为）
  const active = (group.tabs || []).find(t => t.id === group.activeId);
  if (active && active.id !== 'settings' && active.ext !== 'settings') {
    const dup = reactive({ id: uid('tab'), title: active.title, ext: active.ext, path: active.path, dirty: false, content: active.content, preview: false });
    newGroup.tabs.push(dup);
    newGroup.activeId = dup.id;
  }

  focusedGroupId.value = newGroup.id;
  nextTick().then(() => {
    // ensureEditorForGroup 调用已移除 - 现在由 TabContent 组件处理
        // ensureEditorForGroup 调用已移除 - 现在由 TabContent 组件处理
    try {
      if (group.activeId) {
        const t = group.tabs.find(x => x.id === group.activeId);
        // attachModelForTab 调用已移除 - 现在由 TabContent 组件处理
      }
      if (newGroup.activeId) {
        const t2 = newGroup.tabs.find(x => x.id === newGroup.activeId);
        // attachModelForTab 调用已移除 - 现在由 TabContent 组件处理
      }
    } catch {}
  });
}

function splitFocused(direction='row') {
  splitGroup(focusedGroupId.value, direction);
}

async function openFileInFocused(path, opts = {}) {
  editorStore.setActiveFile(path);
  let group = findGroup(layoutRoot.value, focusedGroupId.value) || layoutRoot.value;
  const name = String(path || '').split(/[\\/]/).pop() || 'Untitled';
  const ext = (name.split('.').pop() || '').toLowerCase();
  
  // 处理编辑器模式选项
  const { forcePlainEditor, preferDiffMode } = opts;
  // 特殊情况：打开设置标签页
  if (path === 'settings' || ext === 'settings') { openSettingsTab(); return; }
  // 添加到最近文件（排除设置等特殊情况）
  if (path && path !== 'settings' && ext !== 'settings') {
    try { addRecentFile(String(path)); } catch {}
  }
  // 如果请求则重用预览标签页
  if (opts && opts.preview) {
    let previewTab = (group.tabs || []).find(t => t.preview === true);
    if (!previewTab) {
      const id = uid('tab');
      previewTab = reactive({ id, title: name, ext, path: String(path || ''), dirty: false, content: '', preview: true });
      
      // AI时间线感知：检查文件是否有AI记录，智能选择编辑器模式
      const hasAiRecord = await globalAiWatcher.hasAiRecord(String(path || ''));
      if (hasAiRecord && !forcePlainEditor) {
        const latestEntry = await globalAiWatcher.getLatestAiRecord(String(path || ''));
        modeController.switchToAiDiffMode(previewTab, latestEntry, 'auto');
        console.log('[AI Timeline] Auto-enabled diff mode for preview tab with AI record:', path);
      }
      
      group.tabs.push(previewTab);
    } else {
      previewTab.title = name;
      previewTab.ext = ext;
      previewTab.path = String(path || '');
      previewTab.dirty = false;
      previewTab.content = '';
      
      // AI时间线感知：检查文件是否有AI记录，智能选择编辑器模式
      const hasAiRecord = await globalAiWatcher.hasAiRecord(String(path || ''));
      if (hasAiRecord && !forcePlainEditor) {
        const latestEntry = await globalAiWatcher.getLatestAiRecord(String(path || ''));
        modeController.switchToAiDiffMode(previewTab, latestEntry, 'auto');
        console.log('[AI Timeline] Auto-enabled inline diff mode for updated preview tab with AI record:', path);
      } else if (!hasAiRecord && previewTab.showingDiff) {
        // 如果文件没有AI记录但当前显示diff，切换回普通模式
        modeController.switchToNormalMode(previewTab, 'auto');
        console.log('[AI Timeline] Auto-disabled diff mode for updated preview tab without AI record:', path);
      }
    }
    group.activeId = previewTab.id;
    globalActiveId.value = previewTab.id;
    // 立即附加空模型以避免空白编辑器
    // attachModelForTab 调用已移除 - 现在由 TabContent 组件处理
    // 异步加载
    const pkey = String(path || '');
    if (!openingPaths.has(pkey)) {
      openingPaths.add(pkey);
      useWorkspaceStore().readText(pkey)
        .then(txt => { 
          setTabModelContent(group.id, previewTab, String(txt || '')); 
          previewTab.savedContent = String(txt || ''); 
          previewTab.dirty = false;
          // 检查是否需要自动显示 diff
          // 已移除 shouldAutoShowDiff 检查 - 简化编辑器，不再支持diff功能
        if (false) {
            // showDiffForTab 调用已移除 - 现在由 TabContent 组件处理
            // updateDiffContent 调用已移除 - 现在由 TabContent 组件处理
          }
        })
        .catch(() => {})
        .finally(() => openingPaths.delete(pkey));
    } else {
      nextTick(() => {
        // attachModelForTab 调用已移除 - 现在由 TabContent 组件处理
        // 检查是否需要自动显示 diff
        // 已移除 shouldAutoShowDiff 检查 - 简化编辑器，不再支持diff功能
        if (false) {
          // showDiffForTab 调用已移除 - 现在由 TabContent 组件处理
          // updateDiffContent 调用已移除 - 现在由 TabContent 组件处理
        }
      });
    }
    return;
  }

  // 在整个布局中按路径重用标签页
  const key = path;
  try {
    const all = __debugCollectAllTabs(layoutRoot.value, []);
    console.log('[TabReuse] openFileInFocused request', { rawPath: String(path||''), key, tabs: all });
  } catch {}
  const found = findTabByNormalizedPath(layoutRoot.value, key);
  if (found && found.group && found.tab) {
    // 切换焦点到该组并激活标签页
    try {
      console.log('[TabReuse] hit existing tab', { groupId: found.group.id, tabId: found.tab.id, tabPath: found.tab.path, tabKey: found.tab.path });
    } catch {}
    group = found.group;
    group.activeId = found.tab.id;
    focusedGroupId.value = group.id;
    globalActiveId.value = found.tab.id;
    
    // 根据编辑器模式选项调整标签页状态
    if (forcePlainEditor && found.tab.showingDiff) {
      // 强制使用普通编辑器：关闭diff模式
      found.tab.showingDiff = false;
      found.tab.diffModels = null;
      found.tab.systemHistoryDiff = null;
    } else if (preferDiffMode && !found.tab.showingDiff) {
      // 偏好diff模式：如果有系统历史diff数据则显示
      console.log('[openFileInFocused] preferDiffMode logic:', { hasSystemHistoryDiff: !!found.tab.systemHistoryDiff, currentShowingDiff: found.tab.showingDiff });
      if (found.tab.systemHistoryDiff) {
        found.tab.showingDiff = true;
        console.log('[openFileInFocused] Enabled diff mode for existing tab');
      }
    }
    
    nextTick(() => {
      // attachModelForTab 调用已移除 - 现在由 TabContent 组件处理
        // attachModelForTab 调用已移除 - 现在由 TabContent 组件处理
      // 如果需要则自动显示 diff
      // 已移除 shouldAutoShowDiff 检查 - 简化编辑器，不再支持diff功能
      if (false) {
        // showDiffForTab 调用已移除 - 现在由 TabContent 组件处理
      }
    });
    return;
  }
  // 在当前组中以非预览方式打开相同路径时，将现有预览提升为永久标签页
  const prev = (group.tabs || []).find(t => t.preview && t.path === key);
  if (prev) { 
    prev.preview = false; 
    group.activeId = prev.id; 
    globalActiveId.value = prev.id; 
    nextTick(() => {
      // 检查是否需要显示 diff
      // 已移除 shouldAutoShowDiff 检查 - 简化编辑器，不再支持diff功能
      if (false) {
        // showDiffForTab 调用已移除 - 现在由 TabContent 组件处理
        // updateDiffContent 调用已移除 - 现在由 TabContent 组件处理
      } else {
        // attachModelForTab 调用已移除 - 现在由 TabContent 组件处理
      }
    }); 
    return;
  }
  const id = uid('tab');
  const tab = reactive({ id, title: name, ext, path: String(path || ''), dirty: false, content: '', preview: false });
  
  // AI时间线感知：检查文件是否有AI记录，智能选择编辑器模式
  const hasAiRecord = await globalAiWatcher.hasAiRecord(String(path || ''));
  if (hasAiRecord && !forcePlainEditor) {
    // 文件有AI记录且未强制普通编辑器，默认启用内联diff模式
    const latestEntry = await globalAiWatcher.getLatestAiRecord(String(path || ''));
    modeController.switchToAiDiffMode(tab, latestEntry, 'auto');
    console.log('[AI Timeline] Auto-enabled inline diff mode for file with AI record:', path);
  }
  group.tabs.push(tab);
  group.activeId = id;
  globalActiveId.value = id;
  
  const pkey = String(path || '');
  if (!openingPaths.has(pkey)) {
    openingPaths.add(pkey);
    useWorkspaceStore().readText(pkey)
      .then(txt => { 
        console.log('[openFileInFocused] File read success for:', pkey, 'content length:', txt?.length || 0);
        setTabModelContent(group.id, tab, String(txt || '')); 
        tab.savedContent = String(txt || ''); 
        tab.dirty = false;
        // 检查是否需要自动显示 diff
        // 已移除 shouldAutoShowDiff 检查 - 简化编辑器，不再支持diff功能
        if (false) {
          // showDiffForTab 调用已移除 - 现在由 TabContent 组件处理
          // updateDiffContent 调用已移除 - 现在由 TabContent 组件处理
        } else {
          // 只有在不显示 diff 时才 attach 普通模型
          // attachModelForTab 调用已移除 - 现在由 TabContent 组件处理
        }
      })
      .catch((error) => {
        console.error('[openFileInFocused] File read failed for:', pkey, error);
        // 文件读取失败时也尝试 attach 空模型
        // attachModelForTab 调用已移除 - 现在由 TabContent 组件处理
      })
      .finally(() => openingPaths.delete(pkey));
  } else {
    // 如果文件正在加载中，先创建空编辑器
    // 已移除 shouldAutoShowDiff 检查 - 简化编辑器，不再支持diff功能
    if (true) {
      // attachModelForTab call removed - now handled by TabContent component
    }
  }
}

function closeActiveTab() {
  const g = findGroup(layoutRoot.value, focusedGroupId.value) || layoutRoot.value;
  if (!g || !g.activeId) return;
  const idx = g.tabs.findIndex(t => t.id === g.activeId);
  if (idx >= 0) g.tabs.splice(idx, 1);
  g.activeId = g.tabs[0]?.id || null;
  globalActiveId.value = g.activeId;
  if (!g.tabs.length) removeChildAndCollapse(layoutRoot, g.id);
}

async function saveActiveTab() {
  const g = findGroup(layoutRoot.value, focusedGroupId.value) || layoutRoot.value;
  const tab = (g.tabs || []).find(t => t.id === g.activeId);
  if (!tab || !tab.path) return;
  let content = '';
  try { await monaco.editor.getAction('editor.action.formatDocument')?.run?.(); } catch {}
  const uri = getModelUriForTab(tab);
  const key = uri.toString();
  const model = modelsByPath.get(key) || monaco.editor.getModel(uri) || editorsByGroup.get(g.id)?.getModel?.();
  // 优先使用现有模型文本；否则回退到 tab.content
  content = model ? model.getValue() : String(tab.content || '');
  try { content = await formatCode(langFromExt(tab.ext), content, tab.path || null); if (model) model.setValue(content); } catch {}
  
  // 保存前检查内容是否实际发生变化
  const savedContent = String(tab.savedContent || '');
  if (content === savedContent) {
    // 未检测到变化，跳过保存和历史记录
    return;
  }
  
  await invoke('fs_write_text', { path: tab.path, content, createDirs: true });
  try { outputStore.append('File', `Saved: ${tab.path}`); } catch {}
  tab.savedContent = content;
  tab.dirty = false;
  try { await addLocalHistorySnapshot(tab.path, content); } catch {}
}
function saveAllTabs() {
  try {
    const ids = [];
    collectGroupIds(layoutRoot.value, ids);
    for (const gid of ids) {
      const g = findGroup(layoutRoot.value, gid);
      if (!g) continue;
      for (const tab of g.tabs || []) {
        if (!tab.path) continue;
        const uri = getModelUriForTab(tab);
        const model = monaco.editor.getModel(uri);
        const text = model ? model.getValue() : String(tab.content || '');
        
        // 保存前检查内容是否实际发生变化
        const savedContent = String(tab.savedContent || '');
        if (text === savedContent) {
          // 未检测到变化，跳过保存此标签页
          continue;
        }
        
        invoke('fs_write_text', { path: tab.path, content: text, createDirs: true });
        tab.savedContent = text;
        tab.dirty = false;
      }
    }
  } catch {}
}
async function saveActiveTabAs() {
  const g = findGroup(layoutRoot.value, focusedGroupId.value) || layoutRoot.value;
  const tab = (g.tabs || []).find(t => t.id === g.activeId);
  if (!tab) return;
  try {
    const { save } = await import('@tauri-apps/plugin-dialog');
    const target = await save({ defaultPath: tab.path || tab.title || 'Untitled' });
    if (!target) return;
    const uri = getModelUriForTab(tab);
    const model = monaco.editor.getModel(uri);
    const text = model ? model.getValue() : String(tab.content || '');
    await invoke('fs_write_text', { path: target, content: text, createDirs: true });
    tab.path = target;
    tab.title = String(target.split(/[\\/]/).pop() || tab.title);
    tab.savedContent = text;
    tab.dirty = false;
  } catch {}
}
function formatActiveModel() { try { editorsByGroup.get(focusedGroupId.value)?.getAction('editor.action.formatDocument')?.run?.(); } catch {} }
function openSettingsTab() {
  // 确保在整个布局中保持单例
  const existingGroup = findGroupContainingSettings(layoutRoot.value);
  if (existingGroup) {
    existingGroup.activeId = 'settings';
    focusedGroupId.value = existingGroup.id;
    globalActiveId.value = 'settings';
    return;
  }
  const g = findGroup(layoutRoot.value, focusedGroupId.value) || layoutRoot.value;
  if (!g || g.type !== 'group') return;
  g.tabs.push(reactive({ id:'settings', title:'WaCoder Settings', ext:'settings', path:'', dirty:false, content:'' }));
  g.activeId='settings';
  focusedGroupId.value = g.id;
  globalActiveId.value='settings';
}
function newUntitledTab() { const g = findGroup(layoutRoot.value, focusedGroupId.value); if (!g) return; const id=uid('tab'); g.tabs.push(reactive({ id, title:'Untitled', ext:'', path:'', dirty:false, content:'' })); g.activeId=id; globalActiveId.value=id; }

// 编辑器创建/附加
/** Ensure a correct editor instance is attached to the container for the group.
 * If there is a mode switch or container change, previous editor and its disposables are cleaned up.
 */
/** @param {string} groupId */
// ensureEditorForGroup 函数已移除 - 现在由 TabContent 组件处理
/** @param {string} groupId 
 *  @param {EditorTab} tab
 */
// attachModelForTab 和 attachModelForTabInternal 函数已移除 - 现在由 TabContent 组件处理

// 监听设置并应用到所有编辑器
function scheduleLayoutAll() {
  try {
    for (const [gid, ed] of editorsByGroup.entries()) {
      const node = ed && ed.getDomNode ? ed.getDomNode() : null;
      if (!node || !node.isConnected) {
        try { ed?.dispose?.(); } catch {}
        editorsByGroup.delete(gid);
        continue;
      }
      ed.layout?.();
    }
  } catch {}
}
watch(() => editorStore.fontSize, async v => { for (const ed of editorsByGroup.values()) try { ed.updateOptions({ fontSize: Number(v)||13 }); } catch {}; await nextTick(); scheduleLayoutAll(); });
watch(() => editorStore.wordWrap, async v => { const allowed=new Set(['off','on','wordWrapColumn','bounded']); const mode=allowed.has(String(v))?String(v):'off'; for (const ed of editorsByGroup.values()) try { ed.updateOptions({ wordWrap: mode }); } catch {}; await nextTick(); scheduleLayoutAll(); });
watch(() => editorStore.minimapEnabled, async v => { for (const ed of editorsByGroup.values()) try { ed.updateOptions({ minimap: { enabled: !!v } }); } catch {}; await nextTick(); scheduleLayoutAll(); });
watch(() => editorStore.renderWhitespace, async v => { const allowed=new Set(['none','boundary','selection','trailing','all']); const mode=allowed.has(String(v))?String(v):'selection'; for (const ed of editorsByGroup.values()) try { ed.updateOptions({ renderWhitespace: mode }); } catch {}; await nextTick(); scheduleLayoutAll(); });
watch(() => editorStore.tabSize, async v => { for (const ed of editorsByGroup.values()) try { ed.getModel()?.updateOptions?.({ tabSize: Number(v)||2 }); } catch {}; await nextTick(); scheduleLayoutAll(); });
watch(() => editorStore.insertSpaces, async v => { for (const ed of editorsByGroup.values()) try { ed.getModel()?.updateOptions?.({ insertSpaces: !!v }); } catch {}; await nextTick(); scheduleLayoutAll(); });
watch(() => editorStore.readOnly, async v => { for (const ed of editorsByGroup.values()) try { ed.updateOptions({ readOnly: !!v }); } catch {}; await nextTick(); scheduleLayoutAll(); });

// 当底部面板填充发生变化时触发重新布局（例如面板打开/高度变化）
watch(() => props.bottomPad, async () => { await nextTick(); scheduleLayoutAll(); });

// 暴露 API
/**
 * 关闭指定分组中除给定 tabId 外的其他所有标签。
 * @param {string|number} groupId 分组 ID
 * @param {string} tabId 需要保留的标签 ID
 * @returns {void}
 */
function closeOthersExact(groupId, tabId) {
  const g = findGroup(layoutRoot.value, String(groupId));
  if (!g) return;
  g.tabs = (g.tabs || []).filter(t => t.id === tabId);
  g.activeId = tabId;
  focusedGroupId.value = g.id;
  globalActiveId.value = tabId;
}
/**
 * 关闭指定分组中给定标签右侧的所有标签。
 * @param {string|number} groupId 分组 ID
 * @param {string} tabId 参考的标签 ID
 * @returns {void}
 */
function closeRightExact(groupId, tabId) {
  const g = findGroup(layoutRoot.value, String(groupId));
  if (!g) return;
  const idx = g.tabs.findIndex(t => t.id === tabId);
  if (idx >= 0) g.tabs = g.tabs.slice(0, idx + 1);
  g.activeId = tabId;
  focusedGroupId.value = g.id;
  globalActiveId.value = tabId;
}

// 存储装饰ID以便清理
const searchDecorations = new Map(); // groupId -> [decorationIds]

async function revealLocation(path, line, column, highlightLength = 0, endLine, endCol, context = {}) {
  try {
    if (!path) return;
    
    // 根据上下文决定是否需要特殊的编辑器模式
    const { source, action, preferDiff } = context;
    
    // 如果来源是搜索且有高亮需求，确保使用普通编辑器模式
    if (source === 'search' && (highlightLength > 0 || (endLine && endCol))) {
      // 搜索结果定位：确保文件以普通编辑器模式打开，便于高亮显示
      await openFileInFocused(String(path), { forcePlainEditor: true });
    } else if (source === 'timeline' && action === 'diff') {
      // 时间线diff调用：偏好diff模式
      await openFileInFocused(String(path), { preferDiffMode: true });
    } else if (preferDiff) {
      // 显式偏好diff模式
      await openFileInFocused(String(path), { preferDiffMode: true });
    } else {
      // 默认行为：普通文件打开
      await openFileInFocused(String(path));
    }

    let tries = 0;
    const maxTries = 100;
    const tryReveal = () => {
      if (openingPaths.has(String(path))) {
        if (++tries < maxTries) setTimeout(tryReveal, 50);
        return;
      }

      const g = findGroup(layoutRoot.value, focusedGroupId.value) || layoutRoot.value;
      const tab = (g.tabs||[]).find(t => String(t.path)===String(path));
      if (!tab) {
        if (++tries < maxTries) setTimeout(tryReveal, delay);
        return;
      }

      const ed = editorsByGroup.get(g.id);
      const model = ed?.getModel?.();
      const dom = ed?.getDomNode?.();
      if (!ed || !model || !dom) {
        if (++tries < maxTries) setTimeout(tryReveal, delay);
        return;
      }

      const lineNumber = Math.max(1, Number(line)||1);
      const columnNumber = Math.max(1, Number(column)||1);

      // 清除之前的搜索装饰
      const existingDecorations = searchDecorations.get(g.id) || [];
      if (existingDecorations.length > 0) {
        try { ed.deltaDecorations(existingDecorations, []); } catch {}
        searchDecorations.delete(g.id);
      }

      let range;
      if (endLine && endCol) {
        range = new monaco.Range(lineNumber, columnNumber, Math.max(1, Number(endLine)), Math.max(1, Number(endCol)));
      } else if (highlightLength > 0) {
        range = new monaco.Range(lineNumber, columnNumber, lineNumber, columnNumber + Math.max(1, highlightLength));
      } else {
        range = new monaco.Range(lineNumber, columnNumber, lineNumber, columnNumber);
      }

      try {
        // 根据上下文调整定位行为
        if (source === 'search' && (highlightLength > 0 || (endLine && endCol))) {
          // 搜索结果：设置选中范围并添加高亮装饰
          ed.setSelection(range);
          const decorations = ed.deltaDecorations([], [{
            range: range,
            options: {
              className: 'search-highlight',
              isWholeLine: false,
              overviewRuler: {
                color: '#f97316',
                position: monaco.editor.OverviewRulerLane.Right
              },
              minimap: {
                color: '#f97316',
                position: monaco.editor.MinimapPosition.Inline
              }
            }
          }]);
          searchDecorations.set(g.id, decorations);
          ed.revealRangeInCenter(range, monaco.editor.ScrollType.Smooth);
        } else {
          // 其他情况：普通定位
          ed.setSelection(range);
          ed.revealRangeInCenter(range, monaco.editor.ScrollType.Smooth);
        }
        ed.focus();
      } catch {}
    };

    setTimeout(tryReveal, 0);
  } catch {}
}

async function restoreFile({ resourceUri, source, id }) {
  try {
    await fileOpsService.restoreFile({ resourceUri, source, id });

    // 同步更新已打开编辑器的模型内容（监听服务层事件后也会处理，这里保守兜底一次）
    try {
      const groupIds = [];
      collectGroupIds(layoutRoot.value, groupIds);
      for (const gid of groupIds) {
        const g = findGroup(layoutRoot.value, gid);
        if (!g) continue;
        const tab = (g.tabs || []).find(t => String(t.path) === String(resourceUri));
        if (tab) {
          // 获取最新内容（避免在服务层中传递大文本，这里直接读取模型或从磁盘读取）
          const model = monaco.editor.getModels().find(m => m.uri.fsPath === resourceUri);
          if (model) {
            // 如果模型存在，让模型从文件重新加载：简单方案是直接重置模型的值
            try {
              const { invoke } = await import('@tauri-apps/api/core');
              const fresh = await invoke<string>('fs_read_text', { path: resourceUri });
              setTabModelContent(gid, tab, String(fresh || ''));
            } catch {}
          }
        }
      }
    } catch (e) {
      console.warn('Failed to update open editor content after restore:', e);
    }
  } catch (err) {
    console.error('Error restoring file:', err);
  }
}

async function diffFile(args) {
  const { resourceUri, id, source } = args;
  
  // 获取历史内容
  let originalContent = '';
  if (source === 'git') {
    originalContent = await getGitHistoryContent(resourceUri, id);
  } else {
    originalContent = await getLocalHistoryContent(resourceUri, id);
  }

  // 获取当前文件内容
  let currentContent = '';
  try {
    currentContent = await useWorkspaceStore().readText(resourceUri) || '';
  } catch (e) {
    console.warn('Failed to read current file content:', e);
  }

  // 使用内联diff模式显示历史对比
  // 首先打开文件
  await openFileInFocused(resourceUri, { preview: false });
  await nextTick();

  const group = findGroup(layoutRoot.value, focusedGroupId.value) || layoutRoot.value;
  if (!group) {
    console.warn('diffFile: no group found');
    return;
  }

  const tab = group.tabs.find(t => String(t.path) === String(resourceUri));
  if (!tab) {
    console.warn('diffFile: no tab found for path:', resourceUri);
    return;
  }

  // 设置内联diff模式
  tab.showingDiff = true;
  tab.diffSideBySide = false; // 使用内联模式
  tab.systemHistoryDiff = {
    source,
    id,
    originalContent,
    resourceUri
  };

  console.log('[Timeline Diff] Enabled inline diff mode for timeline comparison:', {
    path: resourceUri,
    source,
    id
  });
}

// 注意：时间线标签页功能已移除，改为使用内联diff模式
// 如需查看AI变更历史，请使用时间线侧边栏面板

// 新增：基于 AI 时间线条目内容显示 Diff（beforeContent vs afterContent）
async function diffAiEntry(args) {
  const path = String(args?.path || '');
  let before = args?.beforeContent;
  let after = args?.afterContent;

  // 如果未提供内容，尝试从 AI 时间线获取最新条目
  if ((!before || !after) && path) {
    try {
      const latest = await aiTimelineStore.getLatestEntryForFile(path);
      if (latest) {
        before = latest.beforeContent;
        after = latest.afterContent;
      }
    } catch {}
  }

  if (typeof before !== 'string' || typeof after !== 'string') {
    console.warn('diffAiEntry: missing before/after content');
    return;
  }

  // 基于现有标签系统打开文件并显示 diff
  await openFileInFocused(path, { preview: false });
  await nextTick();

  const g = findGroup(layoutRoot.value, focusedGroupId.value) || layoutRoot.value;
  if (!g) { console.warn('diffAiEntry: no group'); return; }
  let tab = (g.tabs || []).find(t => String(t.path) === String(path)) || (g.tabs || []).find(t => t.id === g.activeId);
  if (!tab) { console.warn('diffAiEntry: no tab for path'); return; }

  // 创建AI时间线条目
  const aiEntry = {
    id: `ai-${Date.now()}`,
    path: String(path || ''),
    beforeContent: String(before || ''),
    afterContent: String(after || ''),
    tool: 'timeline',
    ts: Date.now(),
    kind: 'modify'
  };

  // 使用编辑器模式控制器切换到AI diff模式
  modeController.switchToAiDiffMode(tab, aiEntry, 'manual');
  
  console.log('[AI Diff] Enabled inline diff mode for existing tab:', {
    tabId: tab.id,
    path: tab.path,
    hasAiStatus: !!tab.aiStatus,
    inlineMode: !tab.diffSideBySide
  });
}
defineExpose({ openSettingsTab, newUntitledTab, closeActiveTab, openFile: openFileInFocused, openFileTab: openFileInFocused, saveActiveTab, saveActiveTabAs, saveAllTabs, formatActiveModel, splitFocused, revealLocation, __closeOthersExact: closeOthersExact, __closeRightExact: closeRightExact, restoreFile, diffFile, diffAiEntry });

// 递归组件
const LayoutNode = defineComponent({
  name: 'LayoutNode',
  props: { node: { type: Object, required: true } },
  setup(p) {
    const localRefs = ref({});
    function setGroupEl(id, el) {
      if (!localRefs.value) localRefs.value = {};
      localRefs.value[id] = el;
      if (el) {
        containerByGroup.set(id, el);
        // 延迟编辑器创建/模型附加以避免在渲染期间改变响应式状态
        setTimeout(() => {
          try {
            // ensureEditorForGroup 调用已移除 - 现在由 TabContent 组件处理
            const g = findGroup(layoutRoot.value, id);
            if (g && g.activeId) {
              const t = g.tabs.find(x => x.id === g.activeId);
              // attachModelForTab 调用已移除 - 现在由 TabContent 组件处理
            }
            scheduleLayoutAll();
          } catch {}
        }, 0);
      } else {
        // 容器卸载时清理编辑器以避免过时绑定
        try { const ed = editorsByGroup.get(id); ed?.dispose?.(); } catch {}
        editorsByGroup.delete(id);
        containerByGroup.delete(id);
      }
    }
    onBeforeUnmount(() => {
      if (p.node.type === 'group') {
        const ed = editorsByGroup.get(p.node.id);
        try { ed?.dispose?.(); } catch {}
        editorsByGroup.delete(p.node.id);
        containerByGroup.delete(p.node.id);
      }
    });
    let activeWatchScheduled = false;
    watch(() => p.node.activeId, (id) => {
      if (p.node.type==='group' && id && !activeWatchScheduled) {
        activeWatchScheduled = true;
        setTimeout(() => {
          try {
            const tab = (p.node.tabs||[]).find(t => t.id===id);
            if (tab) { globalActiveId.value = id; focusedGroupId.value = p.node.id; /* attachModelForTab 调用已移除 - 现在由 TabContent 组件处理 */ }
            // 激活时如果需要则自动显示 diff
            // 已移除 shouldAutoShowDiff 检查 - 简化编辑器，不再支持diff功能
            if (false) {
              // showDiffForTab 调用已移除 - 现在由 TabContent 组件处理
            }
          } finally {
            activeWatchScheduled = false;
          }
        }, 0);
      }
    });
    return { localRefs, layoutRoot, focusedGroupId, globalActiveId, setGroupEl };
  },
  render() {
    const n = this.node;
    if (n.type === 'split') {
      const dir = n.direction === 'row' ? 'flex-row' : 'flex-col';
      const sizes = n.sizes || [];
      return h('div', { class: ['flex w-full h-full', dir] }, n.children.map((child, i) => {
        const style = n.direction === 'row'
          ? { width: (sizes[i] || 0) + '%', height: '100%' }
          : { width: '100%', height: (sizes[i] || 0) + '%' };
        const pane = h('div', { class: 'relative', style }, [
          h(LayoutNode, { node: child, key: String(child.id) }),
          (i < n.children.length - 1)
            ? h('div', {
                class: n.direction === 'row'
                  ? 'absolute top-0 right-0 h-full w-3 z-10 group'
                  : 'absolute left-0 bottom-0 w-full h-3 z-10 group',
                onMousedown: (e) => {
                  e.preventDefault();
                  const start = n.direction === 'row' ? e.clientX : e.clientY;
                  const rect = e.currentTarget.parentElement.parentElement.getBoundingClientRect();
                  const total = n.direction === 'row' ? rect.width : rect.height;
                  const sizesPx = sizes.map(s => (s / 100) * total);
                  const up = () => { window.removeEventListener('mousemove', move); window.removeEventListener('mouseup', up); };
                  const move = (ev) => {
                    const cur = n.direction === 'row' ? ev.clientX : ev.clientY;
                    const delta = cur - start;
                    const a = Math.max(50, sizesPx[i] + delta);
                    const b = Math.max(50, sizesPx[i + 1] - delta);
                    const newSizes = sizes.slice();
                    newSizes[i] = (a / total) * 100;
                    newSizes[i + 1] = (b / total) * 100;
                    n.sizes = newSizes;
                    scheduleLayoutAll();
                  };
                  window.addEventListener('mousemove', move);
                  window.addEventListener('mouseup', up);
                }
              }, [
                // visible 1px line (hover highlight)
                h('div', {
                  class: n.direction === 'row'
                    ? 'absolute right-0 top-0 h-full w-px bg-transparent pointer-events-none group-hover:bg-purple-500/60'
                    : 'absolute left-0 bottom-0 w-full h-px bg-transparent pointer-events-none group-hover:bg-purple-500/60'
                }),
                // invisible enlarged hit area
                h('div', {
                  class: 'absolute inset-0 ' + (n.direction === 'row' ? 'cursor-col-resize' : 'cursor-row-resize') + ' touch-none'
                })
              ])
            : null
        ]);
        return pane;
      }));
    } else {
      const g = n;
      return h('div', { class: 'w-full h-full flex flex-col', onMouseenter: () => { focusedGroupId.value = g.id; } }, [
        h(EditorTabs, {
          groupId: g.id,
          tabs: g.tabs,
          activeId: g.activeId,
          globalActiveId: globalActiveId.value,
          'onUpdate:activeId': (id) => {
            g.activeId = id;
            globalActiveId.value = id;
            focusedGroupId.value = g.id;
            try {
              const tab = (g.tabs || []).find(t => t.id === id);
              if (tab) {
                // No-op: TabContent handles rendering and model attachment
              }
            } catch {}
            setTimeout(() => {
              const t = g.tabs.find(x => x.id === id);
              // Model attachment is handled by TabContent in the simplified editor
            }, 0);
          },
          onClose: (id) => {
            const idx = g.tabs.findIndex(t => t.id === id);
            if (idx >= 0) {
              g.tabs.splice(idx, 1);
              g.activeId = g.tabs[0]?.id || null;
              globalActiveId.value = g.activeId;
              if (!g.tabs.length) removeChildAndCollapse(layoutRoot, g.id);
            }
          },
          onReorder: (payload) => {
            if (!payload || !payload.fromId || !payload.toId) return;
            const fromTab = g.tabs.find(t => t.id === payload.fromId);
            const toTab = g.tabs.find(t => t.id === payload.toId);
            if ((fromTab && (fromTab.id==='settings'||fromTab.ext==='settings')) || (toTab && (toTab.id==='settings'||toTab.ext==='settings'))) return;
            const fromIdx = g.tabs.findIndex(t => t.id === payload.fromId);
            const toIdx = g.tabs.findIndex(t => t.id === payload.toId);
  if (fromIdx < 0 || toIdx < 0 || fromIdx === toIdx) return;
            const [moved] = g.tabs.splice(fromIdx, 1);
            let insertAt = toIdx;
            if (payload.after === true) insertAt = toIdx - (fromIdx < toIdx ? 0 : -1) + 1; // 在目标后
            else insertAt = toIdx - (fromIdx < toIdx ? 1 : 0); // 在目标前
            if (insertAt < 0) insertAt = 0;
            if (insertAt > g.tabs.length) insertAt = g.tabs.length;
            g.tabs.splice(insertAt, 0, moved);
          },
          onContextmenu: (evt, tabArg, gidArg) => {
            const clicked = tabArg || g.tabs.find(t => t.id === g.activeId);
            const groupId = gidArg || g.id;
            try { openContextMenu({ use: 'editor.tab.context' }, { x: evt.clientX, y: evt.clientY }, { tab: clicked, groupId }); } catch {}
          },
          onMove: (payload) => {
            if (!payload || !payload.fromGroupId || !payload.fromId) return;
            // 查找源组
            function findGroupById(node, id) {
              if (!node) return null;
              if (node.type === 'group') return node.id === id ? node : null;
              for (const c of node.children || []) { const r = findGroupById(c, id); if (r) return r; }
              return null;
            }
            const sourceGroup = findGroupById(layoutRoot.value, String(payload.fromGroupId));
            const targetGroup = g;
            if (!sourceGroup || !targetGroup) return;
            if (sourceGroup === targetGroup) return;
            const tabIdx = sourceGroup.tabs.findIndex(t => t.id === payload.fromId);
            if (tabIdx < 0) return;
            const [moved] = sourceGroup.tabs.splice(tabIdx, 1);
            let insertAt = (targetGroup.tabs || []).length;
            if (payload.toId) {
              const tIdx = targetGroup.tabs.findIndex(t => t.id === payload.toId);
              if (tIdx >= 0) insertAt = payload.after ? tIdx + 1 : tIdx;
            }
            if (insertAt < 0) insertAt = 0;
            if (insertAt > targetGroup.tabs.length) insertAt = targetGroup.tabs.length;
            targetGroup.tabs.splice(insertAt, 0, moved);
            targetGroup.activeId = moved.id;
            focusedGroupId.value = targetGroup.id;
            globalActiveId.value = moved.id;
            // attachModelForTab 调用已移除 - 现在由 TabContent 组件处理
            if (!sourceGroup.tabs.length) removeChildAndCollapse(layoutRoot, sourceGroup.id);
          },
          onSplit: () => splitGroup(g.id, 'row'),
          onShowDiff: (groupId, tab) => {
            // 切换为显示 Diff（由 NewTabContent 根据 tab.showingDiff 自动渲染）
            try { tab.showingDiff = true; } catch {}
          },
          onHideDiff: (groupId, tab) => {
            // 隐藏 Diff，恢复普通编辑器视图
            try { tab.showingDiff = false; } catch {}
          },
          onRollbackFile: async (filePath, entry) => {
            // 回滚功能已在精简版编辑器中移除
            console.info('Rollback functionality is not available in the simplified editor.');
          }
        }),
        (g.activeId && g.tabs.find(t => t.id===g.activeId && (t.id==='settings' || t.ext==='settings')))
          ? h('div', { class: 'flex-1 min-h-0 w-full overflow-hidden bg-app-bg' }, [ h(SettingsContent) ])
          : h('div', { class: 'flex-1 min-h-0 w-full h-full' }, [
              // 为每个标签页渲染 TabContent，但只显示活动的标签页
              ...g.tabs.filter(tab => tab.id !== 'settings' && tab.ext !== 'settings').map(tab => 
                h(TabContent, {
                  key: `tab-content-${g.id}-${tab.id}`,
                  tab,
                  groupId: g.id,
                  isActive: tab.id === g.activeId,
                  theme: 'vs-dark', // 待办：从主题存储获取
                  style: {
                    display: tab.id === g.activeId ? 'block' : 'none',
                    width: '100%',
                    height: '100%'
                  },
                  onEditorReady: (payload) => {
                    try {
                      const ed = payload?.modifiedEditor || payload?.editor;
                      if (ed) {
                        // 记录当前分组的可用编辑器实例（若为 diff，优先使用 modified 编辑器）
                        editorsByGroup.set(g.id, ed);
                        // 当编辑器销毁时自动清理映射
                        try {
                          const disp = ed.onDidDispose?.(() => { try { editorsByGroup.delete(g.id); } catch {} });
                          if (disp) {
                            const arr = editorDisposablesByGroup.get(g.id) || [];
                            arr.push(disp);
                            editorDisposablesByGroup.set(g.id, arr);
                          }
                        } catch {}
                      }
                    } catch {}
                  },
                  onContentChange: (content) => {
                    console.log('[EditorArea] onContentChange called for tab:', tab.id);
                    console.log('[EditorArea] New content length:', content?.length || 0);
                    console.log('[EditorArea] Saved content length:', (tab.savedContent || '').length);
                    console.log('[EditorArea] Content equals saved?', content === (tab.savedContent || ''));
                    
                    const isDirty = content !== (tab.savedContent || '');
                    console.log('[EditorArea] Setting dirty to:', isDirty);
                    
                    // 使用响应式赋值更新标签页内容和脏状态
                    Object.assign(tab, {
                      content: content,
                      dirty: isDirty
                    });
                    
                    console.log('[EditorArea] Tab after update - dirty:', tab.dirty, 'content length:', tab.content?.length || 0);
                  },
                  onCursorChange: () => {
                    // no-op
                  },
                  // 当 diff 更新时（例如通过系统时间线以 Diff 打开），可选择性地自动定位到第一个变更
                  onDiffUpdated: (changes) => {
                    try {
                      if (!Array.isArray(changes) || changes.length === 0) return;
                      
                      // 检查是否应该自动选中变更内容
                      // 只有在特定场景下才自动选中，避免用户编辑时的干扰
                      const shouldAutoSelect = (
                        // 当前标签页是通过时间线打开的diff模式
                        t?.systemHistoryDiff?.originalContent !== undefined ||
                        // 或者是刚刚切换到diff模式的情况
                        t?.justSwitchedToDiff === true
                      );
                      
                      if (!shouldAutoSelect) {
                        console.log('[EditorArea] Diff更新但跳过自动选中，避免干扰用户编辑');
                        return;
                      }
                      
                      const ed = editorsByGroup.get(g.id);
                      if (!ed || typeof ed.setSelection !== 'function') return;
                      const first = changes[0];
                      const mStart = Number(first?.modifiedStartLineNumber || 0);
                      const mEnd = Number(first?.modifiedEndLineNumber || mStart);
                      if (!mStart || mStart < 1) return;
                      const model = ed.getModel?.();
                      const endCol = model?.getLineMaxColumn ? model.getLineMaxColumn(mEnd) : 1;
                      const sel = { startLineNumber: mStart, startColumn: 1, endLineNumber: mEnd, endColumn: endCol || 1 };
                      try { ed.setSelection(sel); } catch {}
                      try { ed.revealRangeInCenter?.(sel); } catch { try { ed.revealLineInCenter?.(mStart); } catch {} }
                      
                      // 清除标记，避免重复自动选中
                      if (t?.justSwitchedToDiff) {
                        delete t.justSwitchedToDiff;
                      }
                    } catch {}
                  },
                  // 处理接受变更
                  onAcceptChange: async ({ change, index, tab: changeTab }) => {
                    try {
                      console.log('EditorArea: 接受变更', change, index, changeTab?.path);
                      if (!changeTab) return;

                      // 基线文本（original）优先来自 systemHistoryDiff，否则回退到 latestEntry.beforeContent 或 savedContent
                      const baseline = String(
                        changeTab.systemHistoryDiff?.originalContent ??
                        changeTab.aiStatus?.latestEntry?.beforeContent ??
                        changeTab.savedContent ??
                        ''
                      );
                      const current = String(changeTab.content ?? '');

                      const origLines = baseline.split(/\r?\n/);
                      const modLines = current.split(/\r?\n/);

                      const oStart = Number(change?.originalStartLineNumber || 0);
                      const oEnd = Number(change?.originalEndLineNumber || 0);
                      const mStart = Number(change?.modifiedStartLineNumber || 0);
                      const mEnd = Number(change?.modifiedEndLineNumber || 0);

                      const clampIndex = (v, len) => Math.max(0, Math.min((v|0), len));

                      // 要写入到基线的替换片段：来自 modified 端对应范围
                      const hasModRange = mStart > 0 && mEnd > 0 && mEnd >= mStart;
                      const replacement = hasModRange ? modLines.slice(mStart - 1, mEnd) : [];

                      let newOrigLines = origLines.slice();
                      if (oStart > 0 && oEnd >= oStart) {
                        // 常规：替换 original 指定范围
                        const startIdx = clampIndex(oStart - 1, newOrigLines.length);
                        const delCount = Math.max(0, Math.min(newOrigLines.length - startIdx, oEnd - oStart + 1));
                        newOrigLines.splice(startIdx, delCount, ...replacement);
                      } else {
                        // 纯新增（original 为 0 行）：在合理锚点处插入 replacement
                        const insertAt = clampIndex((mStart > 0 ? mStart - 1 : newOrigLines.length), newOrigLines.length);
                        newOrigLines.splice(insertAt, 0, ...replacement);
                      }

                      const newBaseline = newOrigLines.join('\n');

                      // 写回到 systemHistoryDiff，使 diff 更新
                      const oldSys = changeTab.systemHistoryDiff || {};
                      changeTab.systemHistoryDiff = {
                        source: oldSys.source || 'ai-timeline',
                        id: oldSys.id || (changeTab.path || changeTab.id || 'unknown'),
                        originalContent: newBaseline,
                        resourceUri: oldSys.resourceUri || (changeTab.path || '')
                      };

                      // 保持 Diff 模式
                      changeTab.showingDiff = true;
                    } catch (error) {
                      console.error('处理接受变更失败:', error);
                    }
                  },
                  // 处理拒绝变更
                  onRejectChange: async ({ change, index, tab: changeTab }) => {
                    try {
                      console.log('EditorArea: 拒绝变更', change, index, changeTab?.path);
                      if (!changeTab) return;

                      // 拒绝：用 original 端的文本覆盖 modified 端对应范围
                      const baseline = String(
                        changeTab.systemHistoryDiff?.originalContent ??
                        changeTab.aiStatus?.latestEntry?.beforeContent ??
                        changeTab.savedContent ??
                        ''
                      );
                      const current = String(changeTab.content ?? '');

                      const origLines = baseline.split(/\r?\n/);
                      const modLines = current.split(/\r?\n/);

                      const oStart = Number(change?.originalStartLineNumber || 0);
                      const oEnd = Number(change?.originalEndLineNumber || 0);
                      const mStart = Number(change?.modifiedStartLineNumber || 0);
                      const mEnd = Number(change?.modifiedEndLineNumber || 0);

                      const clampIndex = (v, len) => Math.max(0, Math.min((v|0), len));

                      // 从 original 端获得用于覆盖的片段
                      const hasOrigRange = oStart > 0 && oEnd > 0 && oEnd >= oStart;
                      const replacement = hasOrigRange ? origLines.slice(oStart - 1, oEnd) : [];

                      let newModLines = modLines.slice();
                      if (mStart > 0 && mEnd >= mStart) {
                        const startIdx = clampIndex(mStart - 1, newModLines.length);
                        const delCount = Math.max(0, Math.min(newModLines.length - startIdx, mEnd - mStart + 1));
                        newModLines.splice(startIdx, delCount, ...replacement);
                      } else if (hasOrigRange) {
                        // modified 为空但 original 有范围 => 在合适位置插入（处理删除回退）
                        const insertAt = clampIndex((oStart > 0 ? oStart - 1 : newModLines.length), newModLines.length);
                        newModLines.splice(insertAt, 0, ...replacement);
                      }

                      const newContent = newModLines.join('\n');
                      changeTab.content = newContent;
                      changeTab.dirty = true;

                      // 保持 Diff 模式
                      changeTab.showingDiff = true;
                    } catch (error) {
                      console.error('处理拒绝变更失败:', error);
                    }
                  }
                })
              )
            ])
      ]);
    }
  }
});

// 内容容器
const contentRef = ref(null);
let unregister = () => {};
onMounted(() => { if (contentRef.value) unregister = registerScrollElement('workspace-h', contentRef.value); });
onBeforeUnmount(() => { try { unregister(); } catch {} for (const ed of editorsByGroup.values()) try { ed.dispose(); } catch {} editorsByGroup.clear(); try { for (const arr of editorDisposablesByGroup.values()) { if (!Array.isArray(arr)) continue; for (const d of arr) try { d?.dispose?.(); } catch {} } } catch {} editorDisposablesByGroup.clear(); });
</script>

<template>
  <div class="flex h-full min-h-0 flex-col" :style="{ height: bottomPad ? `calc(100% - ${bottomPad}px)` : '100%' }">
    <div ref="contentRef" class="flex-1 min-h-0 overflow-hidden bg-app-bg thin-scrollbar scrollbar-compact scrollbar-track" :style="{ marginBottom: bottomPad ? '15px' : '0' }">
      <LayoutNode v-if="layoutRoot" :node="layoutRoot" />
      <!-- When no tabs, render routed workspace content -->
      <div v-if="!layoutRoot || (layoutRoot && layoutRoot.type==='group' && !(layoutRoot.tabs||[]).length)" class="h-full">
        <slot />
      </div>
      
      </div>
    </div>
  
</template>

<style>
/* 搜索结果高亮样式 */
.search-highlight {
  background-color: rgba(249, 115, 22, 0.3) !important;
  border: 1px solid rgba(249, 115, 22, 0.6) !important;
  border-radius: 2px !important;
}

/* 深色主题下的搜索高亮 */
.dark .search-highlight {
  background-color: rgba(249, 115, 22, 0.25) !important;
  border-color: rgba(249, 115, 22, 0.5) !important;
}
</style>


